import React, { useState, useRef } from "react"
import {
  TOOLS_PANEL_HEIGHT,
  MAJOR_GRID_SPACING,
  MINOR_GRID_SPACING,
  m4Identity,
  SIM_TIME,
  SIM_TIME_MINUTE,
  SIM_TIME_MS,
  speedRatio,
  settings,
  defaultWorkspace,
  dimensions,
  gcodeTxt,
  defaultCamera,
} from "./assets/constant"
import Grid from "./assets/Grid"
import { fileToBlob } from "./assets/tool"
import { parseGcode, relativePosiParsedGcode } from "./assets/tmpParseGcode"
import { DrawCommands } from "./assets/draw-commands"
import { GcodePreview } from "./assets/draw-commands/GcodePreview"
import {
  drawScene,
  calcCamera,
  initBuffers,
  drawTaskPreview,
  generateProgramInfo,
  objectHasMatchingFields,
} from "./assets/api"
import "./App.css"
const width = 400
const height = 375
class App extends React.Component {
  fileRef = React.createRef()
  camera
  isUpdating
  canvas
  gl
  gcodePreview
  gcodeString
  constructor() {
    super()
    // TODO: make config interface
    this.canvas = null
    this.position = [0, 0]
    this.grid = new Grid()
    const workarea = "fbb1b"
    const width = 400
    const height = 375
    defaultCamera.eye = [width / 2, height / 2, 300]
    defaultCamera.center = [width / 2, height / 2, 0]
    settings.machineWidth = width
    settings.machineHeight = height

    this.camera = {
      fovy: 0,
      perspective: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
      view: [
        0.0029545078577336644, 0, 0, 0, 0, 0.005066666666666666, 0, 0, 0, 0,
        -0.001000050002500125, 0, -0.5909015715467328, 0.95,
        -0.7000850042502125, 1,
      ],
      viewInv: [
        338.46584546472565, 0, 0, 0, 0, 197.3684210526316, 0, 0, 0, 0,
        -999.9499999999999, 0, 199.99999999999997, -187.5, -700.0499999999998,
        1,
      ],
      scale: 2.381333333333333,
    }
    // this.camera = calcCamera({
    //   viewportWidth: dimensions.width,
    //   viewportHeight: dimensions.height,
    //   fovy: defaultCamera.fovy,
    //   near: 0.1,
    //   far: 2000,
    //   eye: defaultCamera.eye,
    //   center: defaultCamera.center,
    //   up: defaultCamera.up,
    //   showPerspective: defaultCamera.showPerspective,
    //   machineX: settings.machineBottomLeftX,
    //   machineY: settings.machineBottomLeftY,
    // })
    this.pointers = []
    this.moveStarted = false
    this.adjustingCamera = false
    this.gcodePreview = new GcodePreview()
    this.simTimeMax = 1
    this.timeDisplayRatio = 1

    this.drawGcodeState = {}

    this.state = {
      width: 1437,
      height: 842,
      camera: defaultCamera,
      workspace: defaultWorkspace,
      isInverting: false,
      speedLevel: 1,
      playState: 0,
    }
  }

  // componentDidMount() {
  //   this.resetView()
  // }
  handleSimTimeChange = (value) => {
    const { workspace } = this.state
    this.position = this.gcodePreview.getSimTimeInfo(Number(value)).position
    this.setState({
      workspace: {
        ...workspace,
        simTime: Number(value),
      },
    })
  }
  resetView = () => {
    const { width, height } = this.state
    const { machineWidth, machineHeight } = settings
    const scale = Math.min(width / machineWidth, height / machineHeight) * 0.95
    const cameraHeight = 300
    let newFovy = 2 * Math.atan(height / (2 * cameraHeight * scale))
    newFovy = Math.max(0.1, Math.min(Math.PI - 0.1, newFovy))
    this.setCameraAttrs({
      eye: [machineWidth / 2, -machineHeight / 2, cameraHeight],
      center: [machineWidth / 2, -machineHeight / 2, 0],
      fovy: newFovy,
    })
  }
  setCanvas = (canvas) => {
    if (this.canvas === canvas) return
    this.canvas = canvas
    if (this.drawCommands) {
      this.drawCommands.destroy()
      this.drawCommands = null
    }
    if (!canvas) return
    if (!this.camera) return
    let gl
    this.gl = gl = canvas.getContext("webgl", {
      alpha: true,
      depth: true,
      preserveDrawingBuffer: true,
    })
    this.drawCommands = new DrawCommands(gl)

    const draw = () => {
      if (!this.canvas) {
        return
      }

      if (settings.toolDisplayCache) {
        if (this.isUpdating) {
          this.isUpdating = false
        } else {
          requestAnimationFrame(draw)
          return
        }
      }
      gl.viewport(0, 0, canvas.width, canvas.height)
      gl.clearColor(1.0, 1.0, 1.0, 1)
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
      gl.enable(gl.BLEND)
      this.drawFlat(canvas, gl)
      requestAnimationFrame(draw)
    }
    draw()
  }
  updateGcode = () => {
    // const result = gcodeTxt.target.result.split("\n")
    const fileReader = new FileReader()
    fileReader.onloadend = (e) => {
      const res = e.target.result.split("\n")
      res.splice(4, 0, "G1 X0 Y0")
      const result = relativePosiParsedGcode(res)

      this.gcodeString = result.join("\n")
      const parsedGcode = parseGcode(this.gcodeString)
      this.gcodePreview.setParsedGcode(parsedGcode)
      this.simTimeMax =
        Math.ceil(
          (this.gcodePreview.g1Time + this.gcodePreview.g0Time) /
            SIM_TIME_MINUTE
        ) *
          SIM_TIME_MINUTE +
        SIM_TIME_MINUTE / 2
      this.timeDisplayRatio = 100 / (60 * this.simTimeMax)
      this.handleSimTimeChange(this.simTimeMax)
    }
    fileReader.readAsText(this.fileRef.current.files[0])
  }

  drawFlat = (canvas, gl) => {
    const { workspace, isInverting } = this.state
    const programInfo = generateProgramInfo(gl)
    const showRemaining = false
    const buffer = initBuffers(
      gl,
      settings.machineWidth,
      settings.machineHeight
    )

    drawScene(
      gl,
      programInfo,
      buffer,
      this.camera,
      isInverting,
      workspace.showTraversal,
      showRemaining
    )

    this.grid.draw(this.drawCommands, {
      perspective: this.camera.perspective,
      view: this.camera.view,
      width: settings.machineWidth,
      height: settings.machineHeight,
      minor: Math.max(settings.toolGridMinorSpacing, 0.1),
      major: Math.max(settings.toolGridMajorSpacing, 1),
    })
    drawTaskPreview(
      this.gcodePreview,
      this.drawGcodeState,
      canvas,
      this.drawCommands,
      workspace,
      this.camera,
      { isInverting, showTraversal: workspace.showTraversal }
    )
  }
  setCameraAttrs = (attrs) => {
    const { camera } = this.state

    this.setState(
      {
        camera: {
          ...camera,
          ...attrs,
        },
      },
      this.setCamera
    )
  }
  onCanvasClick = (e) => this.fileRef.current.click()
  render() {
    return (
      <div id="container">
        <canvas
          width="1612"
          height="940"
          style={{ width: "1612px", height: "940px" }}
          ref={this.setCanvas}
          onClick={this.onCanvasClick}
        />
        <div style={{ marginTop: 10 }}>
          <input
            style={{ display: "none" }}
            type="file"
            id="fileInput"
            ref={this.fileRef}
            onChange={this.updateGcode}
          />
        </div>
      </div>
    )
  }
}

export default App
